Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support batched transactions #223

Merged
merged 29 commits into from
Apr 30, 2024
Merged

Support batched transactions #223

merged 29 commits into from
Apr 30, 2024

Conversation

devbugging
Copy link
Contributor

@devbugging devbugging commented Apr 30, 2024

Closes: #224

Description

Add support for multiple transactions in a single block, which is now possible due to EVM.batchRun(). This PR adds tests blocks with multiple transactions, which was copied from PR made by @m-Peter #222
And adds logic to ingest such blocks.


For contributor use:

  • Targeted PR against master branch
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the standards mentioned here.
  • Updated relevant documentation
  • Re-reviewed Files changed in the Github PR explorer
  • Added appropriate labels

Summary by CodeRabbit

  • New Features

    • Enhanced event handling in blockchain models with updated decoding processes.
    • Improved log filtering efficiency by enabling batch receipt retrieval.
    • Added dynamic determination of event types based on network ID in subscription services.
    • Introduced new test cases for batch transaction processing and Ethereum blockchain interactions.
  • Bug Fixes

    • Fixed timestamp initialization in blockchain API structures.
    • Corrected error handling in stream services to use appropriate error types.
  • Refactor

    • Updated various models and services to utilize new import paths and dependencies for handling blockchain events and transactions.
    • Refactored receipt storage to handle multiple receipts per block efficiently.
  • Tests

    • Expanded test coverage to include new scenarios for blockchain interactions and transaction processing.

@devbugging devbugging self-assigned this Apr 30, 2024
Copy link
Contributor

coderabbitai bot commented Apr 30, 2024

Warning

Rate Limit Exceeded

@sideninja has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 11 seconds before requesting another review.

How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.
Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.
Please see our FAQ for further information.

Commits Files that changed from the base of the PR and between 5a910a3 and 08ddfaf.

Walkthrough

The recent updates encompass enhancements to the blockchain application, including upgrading Go versions, refining error handling, and improving data handling for batch transactions. Notable changes involve modifications in the ingestion and indexing processes to support multiple transactions per block and updates to testing frameworks for robustness in handling new functionalities.

Changes

File Path Changes
.github/workflows/ci.yml Updated Go version from '1.20.x' to '1.21.x'.
api/api.go, api/stream.go Updated struct initialization and error handling logic.
bootstrap/bootstrap.go Modified subscriber initialization to include network ID.
models/..., services/ingestion/..., storage/... Major refactoring to support batch transaction handling and improved efficiency in data retrieval and storage.
tests/... Enhanced testing for new functionalities, including batch transactions and blockchain event handling.

Assessment against linked issues

Objective Addressed Explanation
Add support for batched transactions (#224)

Poem

🐇✨
In the land of code and wire,
Changes bloom like spring's first flower.
Batch transactions, now unfurled,
Across the blockchain, a new world.
Celebrate, for progress leaps,
As the diligent rabbit peeps. 🚀🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@devbugging devbugging marked this pull request as ready for review April 30, 2024 17:49
@@ -64,8 +64,7 @@ type ReceiptIndexer interface {
// GetByBlockHeight returns the receipt for the block height.
// Expected errors:
// - errors.NotFound if the receipt is not found
// TODO right now one transaction per block, but this might change in future so the API needs to be updated.
GetByBlockHeight(height *big.Int) (*gethTypes.Receipt, error)
GetByBlockHeight(height *big.Int) ([]*gethTypes.Receipt, error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure consistent error handling for the GetByBlockHeight method.

The GetByBlockHeight method in the ReceiptIndexer interface is expected to handle errors such as errors.NotFound when the receipt is not found. However, the method now returns a slice of receipts, which implies multiple receipts could be associated with a single block height. This change in functionality should be accompanied by an update in the documentation and error handling strategy to reflect the possibility of returning multiple receipts and handling their associated errors.

Comment on lines +68 to +77
receipts, err := r.receipts.GetByBlockHeight(heights[i])
if err != nil {
return nil, err
}

for _, log := range receipt.Logs {
if exactMatch(log, r.criteria) {
logs = append(logs, log)
for _, receipt := range receipts {
for _, log := range receipt.Logs {
if exactMatch(log, r.criteria) {
logs = append(logs, log)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimize log filtering with concurrent processing.

The current implementation of log filtering in the RangeFilter class processes receipts sequentially. Given that log filtering can be a CPU-intensive operation, especially with a large number of receipts, consider implementing concurrent processing of receipts to improve performance. This could involve using worker pools or parallel processing techniques to handle multiple receipts simultaneously.

Comment on lines 33 to 81
// Store receipt in the index.
//
// Storing receipt will create multiple indexes, each receipt has a transaction ID,
// and a block height. We create following mappings:
// - receipt transaction ID => block height bytes
// - receipt block height => list of encoded receipts (1+ per block)
// - receipt block height => list of bloom filters (1+ per block)
func (r *Receipts) Store(receipt *gethTypes.Receipt) error {
r.mux.Lock()
defer r.mux.Unlock()

// convert to storage receipt to preserve all values
rr := (*models.StorageReceipt)(receipt)
val, err := rlp.EncodeToBytes(rr)
if err != nil {
return err
// we must first retrieve any already saved receipts at the provided height,
// and if found we must add to the list, because this method can be called multiple
// times when indexing a single EVM height, which can contain multiple receipts
receipts, err := r.getByBlockHeight(receipt.BlockNumber.Bytes())
if err != nil && !errors.Is(err, errs.ErrNotFound) { // anything but not found is failure
return fmt.Errorf("failed to store receipt to height, retrieve errror: %w", err)
}

// add new receipt to the list of all receipts, if the receipts do not yet exist at the
// provided height, the above get will return ErrNotFound (which we ignore) and the bellow
// line will init an empty receipts slice with only the provided receipt
receipts = append(receipts, receipt)

batch := r.store.newBatch()
defer batch.Close()

// convert to storage receipt to preserve all values
storeReceipts := make([]*models.StorageReceipt, len(receipts))
for i, rr := range receipts {
storeReceipts[i] = (*models.StorageReceipt)(rr)
}

val, err := rlp.EncodeToBytes(storeReceipts)
if err != nil {
return err
}

height := receipt.BlockNumber.Bytes()

if err := r.store.set(receiptTxIDToHeightKey, receipt.TxHash.Bytes(), height, batch); err != nil {
return fmt.Errorf("failed to store receipt tx height: %w", err)
}

// todo if there are more transactions per block we need to update this
if err := r.store.set(receiptHeightKey, height, val, batch); err != nil {
return fmt.Errorf("failed to store receipt height: %w", err)
}

// todo support multiple heights
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor the Store method to improve error handling and efficiency.

The Store method in the Receipts class performs multiple operations including retrieving existing receipts, appending new receipts, and committing them to the database. The method currently handles errors at multiple points, which can make the code hard to read and maintain. Consider refactoring this method to separate concerns more clearly, perhaps by breaking it down into smaller helper methods that each handle a specific part of the process. This could improve both readability and error handling.

@m-Peter
Copy link
Collaborator

m-Peter commented Apr 30, 2024

@sideninja I think the committed changes to go.mod & go.sum are causing the CI to fail. This must have been added by the generate make recipe, but they should not be committed I think.

Copy link
Collaborator

@m-Peter m-Peter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Out of diff range and nitpick comments (2)
storage/pebble/receipts.go (2)

33-39: Clarify the documentation for the Store method.

The comment block for the Store method could be more descriptive about how the multiple indexes are created and used. Consider expanding this to include examples or more detailed explanations.


Line range hint 178-201: Refactor error handling for range checks.

- if start.Cmp(end) > 0 {
-     return nil, nil, fmt.Errorf("start is bigger than end: %w", errs.ErrInvalidRange)
- }
- if start.Uint64() > last {
-     return nil, nil, fmt.Errorf(
-         "start value %d is not within the indexed range of [0 - %d]: %w",
-         start,
-         last,
-         errs.ErrInvalidRange,
-     )
- }
- if end.Uint64() > last {
-     return nil, nil, fmt.Errorf(
-         "end value %d is not within the indexed range of [0 - %d]: %w",
-         end,
-         last,
-         errs.ErrInvalidRange,
-     )
- }
+ if start.Cmp(end) > 0 || start.Uint64() > last || end.Uint64() > last {
+     return nil, nil, fmt.Errorf("invalid range: start %d or end %d is out of bounds [0 - %d]", start, end, last)
+ }

Consolidate the range checks into a single conditional statement to simplify the code and reduce redundancy. This change makes the function easier to read and maintain.

blockHeight := receipt.BlockNumber.Bytes()
receipts, err := r.getByBlockHeight(blockHeight)
if err != nil && !errors.Is(err, errs.ErrNotFound) { // anything but not found is failure
return fmt.Errorf("failed to store receipt to height, retrieve exisint receipt errror: %w", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve error message clarity.

- return fmt.Errorf("failed to store receipt to height, retrieve exisint receipt errror: %w", err)
+ return fmt.Errorf("failed to store receipt to height, retrieve existing receipt error: %w", err)

The error message contains a typo ("exisint" should be "existing") and another typo in "errror". Correcting these will improve the readability and professionalism of the code.


Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
return fmt.Errorf("failed to store receipt to height, retrieve exisint receipt errror: %w", err)
return fmt.Errorf("failed to store receipt to height, retrieve existing receipt error: %w", err)

Comment on lines 89 to 90
bloomVal, err := rlp.EncodeToBytes(blooms)
if err := r.store.set(bloomHeightKey, height, bloomVal, batch); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Address ineffectual assignment to err.

- bloomVal, err := rlp.EncodeToBytes(blooms)
- if err := r.store.set(bloomHeightKey, height, bloomVal, batch); err != nil {
+ bloomVal, encodeErr := rlp.EncodeToBytes(blooms)
+ if err := r.store.set(bloomHeightKey, height, bloomVal, batch); encodeErr != nil {
  return fmt.Errorf("failed to store bloom height: %w", encodeErr)
}

The variable err is reassigned before its value is checked, which makes the assignment ineffectual. Use a different variable for the error from the rlp.EncodeToBytes function to ensure that it is effectively checked.


Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
bloomVal, err := rlp.EncodeToBytes(blooms)
if err := r.store.set(bloomHeightKey, height, bloomVal, batch); err != nil {
bloomVal, encodeErr := rlp.EncodeToBytes(blooms)
if err := r.store.set(bloomHeightKey, height, bloomVal, batch); encodeErr != nil {

@devbugging devbugging merged commit 4997c78 into main Apr 30, 2024
2 checks passed
@m-Peter m-Peter deleted the gregor/batched-transactions branch July 29, 2024 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

Add support for batched transactions
2 participants